package org.example.mq.mqserver;

import org.example.mq.common.Consumer;
import org.example.mq.common.MqException;
import org.example.mq.dataCenter.ConsumerManager;
import org.example.mq.dataCenter.DiskDataCenter;
import org.example.mq.dataCenter.MemoryDataCenter;
import org.example.mq.mqserver.core.*;
import org.example.mq.mqserver.coreenum.ExchangeType;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/*
* 用户管理硬件数据和软件数据，对之前写的操作进行封装和整合
* */
public class VirtualHost {
    private String virtualName;
    private DiskDataCenter diskDataCenter = new DiskDataCenter();
    private MemoryDataCenter memoryDataCenter = new MemoryDataCenter();
    private Router router = new Router();
    private final Object exchangeLocker = new Object();
    private final Object queueLocker  = new Object();
    private ConsumerManager consumerManager = new ConsumerManager(this);

    public String getVirtualName() {
        return virtualName;
    }

    public DiskDataCenter getDiskDataCenter() {
        return diskDataCenter;
    }

    public MemoryDataCenter getMemoryDataCenter() {
        return memoryDataCenter;
    }

    public VirtualHost(String virtualName) {
        this.virtualName = virtualName;
//        1.进行初始化
        //硬件数据的初始化
        diskDataCenter.init();
//        2.将硬件中的数据加载到内存中（这里只有存在数据恢复才有意义）
        try {
            memoryDataCenter.recovery(diskDataCenter);
        } catch (IOException | MqException | ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("[VirtualHost]  恢复内存数据失败");
        }
    }

    /*
    * 交换机操作
    * */
//    创建交换机（如果存在就返回，不存在就创建）
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType,boolean durable,boolean autoDelete,
                                   Map<String,Object> arguments){

//        1.重新定义交换机名
        exchangeName = virtualName+exchangeName;
        try{
            synchronized (exchangeLocker) {
//        2.判断交换机是否存在
                Exchange existExchange = memoryDataCenter.getExchange(exchangeName);
                if (existExchange != null) {
                    System.out.println("交换机已存在");
                    return true;
                }
//           3.交换机不存在，则需要创建
                Exchange exchange = new Exchange();
                exchange.setName(exchangeName);
                exchange.setType(exchangeType);
                exchange.setDurable(durable);
                exchange.setAutoDelete(autoDelete);
                exchange.setArguments(arguments);
//           4.数据写入硬盘（持久化存储）
                if (durable == true) {
                    diskDataCenter.insertExchange(exchange);
                }
//           5.数据写入内存
                memoryDataCenter.insertExchange(exchangeName, exchange);
                System.out.println("交换机创建成功");
            }
            return true;
        }catch (Exception e){
            System.out.println("交换机创建失败");
            return false;
        }
    }

    public boolean exchangeDelete(String exchangeName){
        exchangeName = virtualName+exchangeName;
        try{
            synchronized (exchangeLocker) {
                Exchange existExchange = memoryDataCenter.getExchange(exchangeName);
                if (existExchange == null) {
                    throw new MqException("交换机不存在，无法删除");
                }
                if (existExchange.isDurable()) {
                    diskDataCenter.deleteExchange(exchangeName);
                }
                memoryDataCenter.deleteExchange(exchangeName);
                System.out.println("交换机删除成功");
            }
            return true;
        } catch (MqException e) {
            System.out.println("交换机删除失败");
            return false;
        }
    }

    /*
    * 队列操作
    * */
    public boolean queueDeclare(String queueName,boolean durable,boolean exclusive,boolean autoDelete,
                                Map<String,Object> arguments){
        queueName = virtualName+queueName;
        try{
            synchronized (queueLocker) {
                MSGQueue existQueue = memoryDataCenter.getQueue(queueName);
                if (existQueue != null) {
                    System.out.println("队列已存在");
                    return true;
                }
                MSGQueue newQueue = new MSGQueue();
                newQueue.setName(queueName);
                newQueue.setDurable(durable);
                newQueue.setExclusive(exclusive);
                newQueue.setAutoDelete(autoDelete);
                newQueue.setArguments(arguments);

                if (durable == true) {
                    diskDataCenter.insertMSGQueue(newQueue);
                }
                memoryDataCenter.insertQueue(newQueue.getName(), newQueue);
                System.out.println("队列创建成功");
            }
            return true;
        } catch (IOException e) {
            System.out.println("队列创建失败");
            return false;
        }
    }

    public boolean queueDelete(String queueName){
        queueName = virtualName+queueName;
        try{
            synchronized (queueLocker) {
                MSGQueue existQueue = memoryDataCenter.getQueue(queueName);
                if (existQueue == null) {
                    throw new MqException("队列不存在，无法删除");
                }

                if (existQueue.isDurable()) {
                    diskDataCenter.deleteMSGQueue(queueName);
                }
                memoryDataCenter.deleteQueue(queueName);
                System.out.println("队列删除成功");
            }
            return true;
        } catch (MqException | IOException e) {
            System.out.println("删除失败");
            e.printStackTrace();
            return false;
        }
    }

    /*
    * 绑定操作
    * */
    public boolean queueBind(String queueName,String exchangeName,String bindingKey){
//        1.重命名
        queueName = virtualName+queueName;
        exchangeName = virtualName+exchangeName;

        try{
            synchronized (exchangeLocker){
                synchronized (queueLocker){
//            2.判断绑定是否存在
                    Binding exitBinding = memoryDataCenter.getBinding(queueName,exchangeName);
                    if(exitBinding != null){
                        throw new MqException("binding 已存在，无法添加");
                    }
//            3.添加绑定关系
                    //验证binding是否合法
                    if(!router.checkBindingKey(bindingKey)){
                        throw new MqException("binding 非法，无法添加");
                    }
                    Binding binding = new Binding();
                    binding.setExchangeName(exchangeName);
                    binding.setQueueName(queueName);
                    binding.setBindingKey(bindingKey);
//            4.验证一下内存中交换机和队列是否存在
                    Exchange exchange = memoryDataCenter.getExchange(exchangeName);
                    if(exchange ==null){
                        throw new MqException("绑定过程中内存交换机不存在");
                    }
                    MSGQueue queue = memoryDataCenter.getQueue(queueName);
                    if(queue==null){
                        throw new MqException("绑定过程中内存队列不存在");
                    }
//            5.写入硬盘（交换机和队列都为持久化）
                    if(exchange.isDurable()&&queue.isDurable()){
                        diskDataCenter.insertBinding(binding);
                    }
//            6.写入内存
                    memoryDataCenter.insertBinding(binding);
                    System.out.println("绑定关系添加成功");

                }
            }
            return true;
        } catch (MqException e) {
            System.out.println("绑定关系添加失败");
            e.printStackTrace();
            return false;
        }
    }


    public boolean queueUnbind(String queueName,String exchangeName){
        exchangeName = virtualName+exchangeName;
        queueName = virtualName+queueName;
        try{
            synchronized (exchangeLocker) {
                synchronized (queueLocker) {
//            1.检查绑定是否存在
                    Binding binding = memoryDataCenter.getBinding(exchangeName, queueName);
                    if (binding == null) {
                        throw new MqException("对应的队列不存在绑定，无法删除");
                    }
/*//            2.检查交换机和队列是否存在
            MSGQueue queue = memoryDataCenter.getQueue(queueName);
            if(queue==null){
                throw new MqException("对应的绑定中队列不存在，无法删除");
            }
            Exchange exchange = memoryDataCenter.getExchange(exchangeName);
            if(exchange==null){
                throw new MqException("对应的绑定中交换机不存在，无法删除");
            }
//            3.删除硬件中的绑定（交换机和队列都是持久化存储，那么绑定也是持久化存储）
            if(exchange.isDurable()&&queue.isDurable()){
                diskDataCenter.deleteBinding(binding);
            }*/

//            3.省去校验，直接删除(就算删除失败，也没有副作用,好处就是方便快捷，避免出现了交换机和队列已经被删除了，但是绑定还在，删除失败)
                    diskDataCenter.deleteBinding(binding);
//            4.删除内存中的绑定
                    memoryDataCenter.deleteBinding(binding);
                    System.out.println("删除绑定成功");
                }
            }
            return true;
        } catch (MqException e) {
            System.out.println("删除绑定失败");
            return false;
        }
    }

    //发送消息到指定队列/交换机中
    public boolean basicPublic(String exchangeName, String routingKey, BasicProperties basicProperties,byte[] body){
        try{
//            1.封装交换机名字
            exchangeName = virtualName+exchangeName;
//            2.检查routingKey是否合法
            if(!router.checkRoutingKey(routingKey)){
                throw new MqException("routingKey非法");
            }
//            3.检查交换机对象是否存在
            Exchange exchange = memoryDataCenter.getExchange(exchangeName);
            if(exchange==null){
                throw new MqException("交换机对象不存在");
            }
//            4.判断交换机类型
            if(exchange.getType() == ExchangeType.DIRECT){
                //交换机类型为直接交换机（routingKey为队列的名称）
                String queueName = virtualName+routingKey;
//                5.构造消息体
                Message message = Message.createMessage(routingKey,body,basicProperties);
//                6.检查队列是否存在
                MSGQueue queue = memoryDataCenter.getQueue(queueName);
                if(queue==null){
                    throw new MqException("发送的队列不存在");
                }
                sendMessage(queue,message);
                return true;
            }else {
                //获取交换机下的所有队列的绑定关系
                ConcurrentHashMap<String,Binding> bindingsMap = memoryDataCenter.getBindings(exchangeName);
                for(Map.Entry<String,Binding> entry:bindingsMap.entrySet()){
                    //根据绑定判断队列是否存在
                    Binding binding = entry.getValue();
                    MSGQueue queue = memoryDataCenter.getQueue(binding.getQueueName());
                    if(queue == null){
                        System.out.println("发送消息时，发现队列不存在");
                        continue;
                    }
                    //队列存在，则构架消息
                    Message message = Message.createMessage(routingKey,body,basicProperties);
//                    根据类型不同选择对应的发送模式
                    if(!router.route(exchange.getType(),binding,message)){
                        continue;
                    }
                    sendMessage(queue,message);
                    return true;
                }
            }
            return true;
        } catch (MqException | IOException e) {
            System.out.println("发送消息失败");
            e.printStackTrace();
            return false;
        }
    }

    private void sendMessage(MSGQueue queue, Message message) throws IOException, MqException {
        int deliverMode = message.getBasicProperties().getDeliverMode();
        if(deliverMode == 2){
            diskDataCenter.sendMessage(queue, message);
        }
        memoryDataCenter.sendMessage(queue, message);
        //通知可以取消息
        consumerManager.notifyConsume(queue.getName());

    }

    /*
    * 订阅消息（添加一个队列的订阅者，队列接收到消息之后，把消息推送给对应的订阅者）
    * */
    public boolean basicConsume(String consumerTag, String queueName, boolean autoAck, Consumer consumer){
        queueName = virtualName+queueName;
        try{
            consumerManager.addConsumer(consumerTag,queueName,autoAck,consumer);
            System.out.println("订阅消息成功");
            return true;
        } catch (Exception e) {
            System.out.println("订阅消息失败");
            return false;
        }
    }

    /*
    * 手动应答
    * */
    public boolean basicAck(String queueName,String messageId){
        queueName = virtualName + queueName;
        try{
            Message message = memoryDataCenter.getMessage(messageId);
            if(message==null){
                throw new MqException("消息不存在  消息手动应答失败");
            }
            MSGQueue queue = memoryDataCenter.getQueue(queueName);
            if(queue == null){
                throw new MqException("队列不存在  消息手动应答失败");
            }
//            删除硬盘数据
            if(message.getBasicProperties().getDeliverMode()==2){
                diskDataCenter.deleteMessage(queue,message);
            }

//            删除内存数据
            memoryDataCenter.deleteMessage(messageId);
            memoryDataCenter.deleteMessageWaitAck(queueName,messageId);
            System.out.println("消息手动应答成功");
            return true;
        } catch (MqException | IOException | ClassNotFoundException e) {
            System.out.println("消息手动应答失败");
            return false;
        }
    }

}
